package lesson2.example3;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;

@SupportedAnnotationTypes({ "lesson2.example3.Getter" })
@SupportedSourceVersion(SourceVersion.RELEASE_8) 
public class GetSetProcessor extends AbstractProcessor {

	private Messager messager;
	private JavacTrees trees;
	private TreeMaker treeMaker;
	private Names names;

	@Override
	public synchronized void init(ProcessingEnvironment pe) {
		super.init(pe);
		messager = pe.getMessager();

		this.trees = JavacTrees.instance(processingEnv);
		Context context = ((JavacProcessingEnvironment) pe).getContext();
		this.treeMaker = TreeMaker.instance(context);
		this.names = Names.instance(context);
	}

	// 生成Get与Set方法的参考文章：https://blog.mythsman.com/2017/12/19/1/
	@Override
	public boolean process(Set<? extends TypeElement> set, RoundEnvironment re) {

		Set<? extends Element> elems = re.getElementsAnnotatedWith(Getter.class);
		TreeTranslator tt = new TreeTranslator() {
			@Override
			public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
				List<JCVariableDecl> jcVariableDeclList = List.nil();

				for (JCTree tree : jcClassDecl.defs) {
					if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
						JCVariableDecl jcVariableDecl = (JCVariableDecl) tree;
						jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
					}
				}

				for (JCVariableDecl jcVariableDecl : jcVariableDeclList) {
					messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + " has been processed");
					jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
				}

				super.visitClassDef(jcClassDecl);
			}

		};
		for (Element element : elems) {
			JCTree jcTree = trees.getTree(element);
			jcTree.accept(tt);
		}
		return true;
	}

	private JCTree.JCMethodDecl makeGetterMethodDecl(JCVariableDecl jcVariableDecl) {

		ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
		JCIdent jci = treeMaker.Ident(names.fromString("this"));
		JCFieldAccess jcf = treeMaker.Select(jci, jcVariableDecl.getName());
		JCReturn jcr = treeMaker.Return(jcf);
		statements.append(jcr);
		JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

		return treeMaker.MethodDef(
					treeMaker.Modifiers(Flags.PUBLIC), 
					getNewMethodName(jcVariableDecl.getName()),
					jcVariableDecl.vartype, 
					List.<JCTypeParameter>nil(), 
					List.<JCVariableDecl>nil(),
					List.<JCExpression>nil(), 
					body, 
					null
				);
	}

	private Name getNewMethodName(Name name) {
		String s = name.toString();
		String mn = "get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length());
		return names.fromString(mn);
	}
}












